#代码的每个小块的顺序与代码内容对应实际文章案例的代码内容与生成图片的顺序


'''1.数据加载与预处理'''

#加载 load_diabetes 数据集，并进行基本的预处理。
######################################################################
#导入需要的包
import pandas as pd
from sklearn.datasets import load_diabetes
import matplotlib.pyplot as plt
#加载数据集
diabetes = load_diabetes()
X = diabetes.data
y = diabetes.target
# 转换为DataFrame以便于处理
df = pd.DataFrame(X, columns=diabetes.feature_names)
df['target'] = y
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
print("数据集前5行:\n", df.head())
print("\n数据集信息:\n")
df.info()
print("\n数据集描述统计:\n", df.describe())
#######################################################################

'''2.数据探索与可视化'''

#计算特征之间的相关性矩阵，使用热力图展示特征之间的相关性
#######################################################################
import seaborn as sns
# 计算特征之间的相关性矩阵
corr_matrix = df.corr()
# 绘制热力图
plt.figure(figsize=(12, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', linewidths=0.5)
plt.title('糖尿病特征相关性矩阵热力图')
plt.show()
#########################################################################
#绘制特征相关性散点图
#########################################################################
# 获取所有特征名称（不包括目标变量）
# 获取所有特征名称（不包括目标变量）
features = diabetes.feature_names

# 创建一个 2x2 的子图网格
fig, axes = plt.subplots(2, 2, figsize=(10, 8))

# 绘制 sex 与 bmi 的散点图
sns.scatterplot(x=df['sex'], y=df['bmi'], ax=axes[0, 0], s=100)
axes[0, 0].set_title('sex vs bmi', fontsize=10)
axes[0, 0].tick_params(axis='both', labelsize=8)

# 绘制 s1 与 s2 的散点图
sns.scatterplot(x=df['s1'], y=df['s2'], ax=axes[0, 1], s=100)
axes[0, 1].set_title('s1 vs s2', fontsize=10)
axes[0, 1].tick_params(axis='both', labelsize=8)

# 绘制 s2 与 s4 的散点图
sns.scatterplot(x=df['s2'], y=df['s4'], ax=axes[1, 0], s=100)
axes[1, 0].set_title('s2 vs s4', fontsize=10)
axes[1, 0].tick_params(axis='both', labelsize=8)

# 绘制 s3 与 s4 的散点图
sns.scatterplot(x=df['s3'], y=df['s4'], ax=axes[1, 1], s=100)
axes[1, 1].set_title('s3 vs s4', fontsize=10)
axes[1, 1].tick_params(axis='both', labelsize=8)

# 隐藏所有子图的图例
for ax in axes.flatten():
#    ax.legend_.remove()
    ax.set_xlabel(ax.get_xlabel(), labelpad=1, fontsize=8)  # 调整横轴标签位置
    ax.set_ylabel(ax.get_ylabel(), labelpad=1, fontsize=8)  # 调整纵轴标签位置

# 调整子图布局
plt.tight_layout()
# 显示图形
plt.show()
##############################################################################
#绘制不同糖尿病严重程度下的体重指数分布
##############################################################################
# 糖尿病严重程度分组
df['target_group'] = pd.cut(df['target'], bins=[0, 100, 200, 350], labels=['低', '中', '高'])
# 绘制箱型图
plt.figure(figsize=(10, 6))
sns.boxplot(x='target_group', y='bmi', data=df, palette=['#228B22','#FFF5EE','#FF4500'])
plt.title('不同糖尿病严重程度下的体重指数分布')
plt.show()
#############################################################################
#不同糖尿病严重程度下的血压分布
##############################################################################
plt.figure(figsize=(10, 6))
sns.boxplot(x='target_group', y='bp', data=df, palette=['#FF4500', '#FFE4E1', '#228B22'])
plt.title('不同糖尿病严重程度下的血压分布')
plt.show()
#############################################################################

'''3.数据预处理与特征选择'''

#检查数据集缺失
#############################################################################
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.tree import DecisionTreeClassifier, plot_tree
# 检查缺失值
print("数据集是否有缺失值：", df.isnull().values.any())
print(df.isnull().sum())
print("target 列的最小值：", df['target'].min())
print("target 列的最大值：", df['target'].max())
# 特征和标签
X = df.drop(['target', 'target_group'], axis=1)
y = df['target_group']
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 特征标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
#############################################################################

'''4.模型训练与评估'''

#绘制决策树
#############################################################################
# 使用 SelectKBest 选择最佳特征
selector = SelectKBest(score_func=f_classif, k=5)
X_train_selected = selector.fit_transform(X_train, y_train)
X_test_selected = selector.transform(X_test)
# 查看选择的特征
selected_features = X.columns[selector.get_support()]
print("选择的特征：", selected_features)
# 创建决策树分类器

#clf = DecisionTreeClassifier(random_state=42,max_depth=3) #限制深度会便于决策树可视化，但是同时会影响模型效果，导致后续结果改变
clf = DecisionTreeClassifier(random_state=42)
clf.fit(X_train_selected, y_train)
# 打印决策树
plt.figure(figsize=(20, 10))
plot_tree(clf, feature_names=selected_features, class_names=clf.classes_, filled=True, rounded=True)
plt.title('决策树结构')
plt.show()
#############################################################################
#绘制混淆矩阵
#############################################################################
import numpy as np
from sklearn.metrics import confusion_matrix, classification_report
# 预测
y_pred = clf.predict(X_test_selected)
# 评估模型
print("混淆矩阵：")
conf_matrix = confusion_matrix(y_test, y_pred)
print(conf_matrix)
print("\n分类报告：")
print(classification_report(y_test, y_pred))
# 绘制比例混淆矩阵热力图
conf_matrix_normalized = conf_matrix.astype('float') / conf_matrix.sum(axis=1)[:, np.newaxis] # 按行归一化
plt.figure(figsize=(8, 6))
sns.heatmap(conf_matrix_normalized, annot=True, fmt='.2f', cmap='Blues', 
            xticklabels=['低', '中', '高'], yticklabels=['低', '中', '高'])
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.title('混淆矩阵（比例）')
plt.show()
#############################################################################
#绘制特征重要性条形图
#############################################################################
# 获取特征重要性
feature_importances = clf.feature_importances_
# 将特征重要性和特征名称组合成一个 DataFrame
importance_df = pd.DataFrame({
    'Feature': selected_features,
    'Importance': feature_importances
})
# 按重要性排序
importance_df = importance_df.sort_values(by='Importance', ascending=False)
# 可视化特征重要性
plt.figure(figsize=(10, 6))
sns.barplot(x=feature_importances, y=selected_features)
plt.xlabel('重要性')
plt.ylabel('特征')
plt.title('特征重要性')
plt.show()
#############################################################################
